import SparkMD5 from 'spark-md5'
import { getUserDataPath } from '@/utils/path.js'
//通用请求封装：发送初始化握手请求
export const postConsole = (options) => {
  return new Promise((resolve, reject) => {
    uni.request({
      url: options.url + '/console',
      method: options.method || 'POST',
      data: options.data || {},
      dataType: 'json',
      header: options.header || {},
      success: (res) => {
        if (res.data.code === '0') {
          resolve(res.data.data)
        } else {
          reject(res.data.msg)
        }
      },
      fail: reject,
    })
  })
}

export default class BigVideoUpload {
  constructor(Setting) {
    this.Setting = Setting//上传配置
  }

  startUpload() {
    this.chunkSize = this.Setting.size//每个分片的大小
    this.pt_md5 = ''//整体视频的MD5
    this.chunks = Math.ceil(this.Setting.byteLength / this.chunkSize)//总分片数
    this.currentChunk = 0//当前上传分片索引
    this.gowith = true//控制是否继续上传
    // 读取首个分片内容，发起握手请求，用整段文件生成 MD5
    this.fileSlice(0, this.Setting.byteLength, file => {
      this.handshake(flag => {
        if (flag) this.loadNext()
        else this.Setting.callback(false)
      }, file)
    })
  }
//  发起握手请求，判断是否已有部分分片上传
  handshake(cbk, firstChunk) {
    const md5 = this.getDataMd5(firstChunk) // 计算首个分片的 md5
    this.pt_md5 = md5
    postConsole({
      url: this.Setting.url,
      data: {
        pt_md5: md5,
        chunks: this.chunks,
        size: this.Setting.byteLength,
        fileName: this.Setting.fileName,
        contentType: this.Setting.type,
        type: 'handshake',
      },
    }).then(res => {
      if (res === 'success') cbk(true)//全部上传
      else {
        this.currentChunk = res//已上传部分，继续上传剩余
        this.loadNext()
      }
    }).catch(err => {
      console.error(err)
      cbk(false)
    })
  }
  //加载下一个分片上传
  loadNext() {
    const p = Math.floor((this.currentChunk * 100) / this.chunks)
    this.drowSpeed(p)//更新进度
    const start = this.currentChunk * this.chunkSize
    const length = Math.min(this.chunkSize, this.Setting.byteLength - start)
    if (this.gowith) {
      this.fileSlice(start, length, file => {
        this.uploadChunk(file)
      })
    }
  }
//上传某个分片
  uploadChunk(data) {
    const fs = uni.getFileSystemManager()//获取文件系统管理器（用于本地读写）
    const md5 = this.getDataMd5(data)//计算当前分片的mD5（唯一标识）

    const basePath=getUserDataPath()
    const dirPath=`${basePath}/video_temp`
    const tempPath=`${dirPath}/${md5}.temp`// 临时文件路径，用于存放该分片
    // const tempPath = `${wx.env.USER_DATA_PATH}/video_temp/${md5}.temp` // 临时文件路径，用于存放该分片
    // 确保临时目录存在
    fs.access({
      path: `${wx.env.USER_DATA_PATH}/video_temp`,
      fail() {
        //如果不存在创建该目录
        fs.mkdirSync(`${wx.env.USER_DATA_PATH}/video_temp`, false)
      }
    })
    fs.writeFile({
      filePath: tempPath,//写入路径（以分片md5命名）
      encoding: 'binary',
      data: data,//当前分片二进制数据
      success: () => {
        // 构造上传所需的表单字段
        const formData = {
          pt_md5: this.pt_md5,
          currentChunk: this.currentChunk,
          chunks: this.chunks,
          type: 'file',
          md5: md5,
          fileName: this.Setting.fileName
        }
        // 上传该分片
        uni.uploadFile({
          url: this.Setting.url,
          filePath: tempPath,// 刚刚写入的临时文件
          name: 'file',
          formData,
          success: res => {
            fs.unlinkSync(tempPath)//上传成功后立即删除临时文件（释放空间）
            const data = JSON.parse(res.data)
            if (data.code === '0') {
              this.currentChunk++//上传成功，准备下一个分片
              if (this.currentChunk < this.chunks) {
                this.loadNext()
              } else {
                this.callback(data.data)//所有分片上传成功，触发回调
              }
            } else {
              this.callback(false)//上传失败，调用失败回调
            }
          },
          // 上传失败处理
          fail: err => {
            console.log('上传失败：', err)
            this.callback(false)
          }
        })
      },
      //写入临时文件失败处理
      fail: err => {
        console.error('分片写入失败：', err)
        this.callback(false)
      }
    })
  }
  // 分片读取函数：从指定位置读取指定长度的二进制数据
  fileSlice(start, length, cbk) {
    uni.getFileSystemManager().readFile({
      filePath: this.Setting.filePath,
      encoding: 'binary',
      position: start,
      length: length,
      success: res => cbk(res.data),//读取成功后，调用回调函数返回读取到的二进制数据
      fail: err => {
        console.error('切片失败', err)
        this.callback(false)//执行失败回调（通知上传中断）
      }
    })
  }
//计算md5值
  getDataMd5(data) {
    const spark = new SparkMD5()
    spark.appendBinary(data)
    return spark.end()
  }
//上传进度回调
  drowSpeed(p) {
    if (typeof this.Setting.drowSpeed === 'function') {
      this.Setting.drowSpeed(p)
    }
  }

  callback(res) {
    if (typeof this.Setting.callback === 'function') {
      this.Setting.callback(res)
    }
  }
}
